package com.zg.nacos.task;

/**
 * <p>Description: </p>
 *
 * @author zhanfeng.liang
 * @since 2025/6/27 20:58
 */

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import cn.hutool.core.util.StrUtil;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.zg.nacos.domain.bo.InstanceInfo;
import com.zg.nacos.domain.entity.MicroNacos;
import com.zg.nacos.manager.InstanceManager;
import com.zg.nacos.manager.NamingServiceManager;
import com.zg.nacos.service.MicroNacosService;
import com.zg.nacos.sys.ActiveStatusEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;

import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
@RequiredArgsConstructor
public class HealthCheckTask {
    // 配置参数
    private static final int MAX_FAILURE_COUNT = 10; // 最大失败次数阈值
    private static final long INSTANCE_DEREGISTER_DELAY = 300000; // 实例注销延迟 (5分钟)
    private static final long SERVICE_DEREGISTER_DELAY = 600000; // 服务注销延迟 (10分钟)
    private static final long HEALTH_CHECK_INTERVAL = 30000; // 健康检查间隔 (30秒)

    @Value("${spring.cloud.nacos.username}")
    private  String username;
    @Value("${spring.cloud.nacos.password}")
    private  String password;
    @Value("${spring.cloud.nacos.server-addr}")
    private  String serverAdd;
    @Autowired
    private WebClient webClient;
    // 依赖注入
    private final InstanceManager instanceManager;
    private final NamingServiceManager namingServiceManager;
    private final RestTemplate restTemplate;
    private final MicroNacosService microNacosService;

    // 待注销服务列表 [serviceKey -> 标记时间]
    private final Map<String, Long> servicesToDeregister = new ConcurrentHashMap<>();

    /**
     * 定时执行健康检查任务
     */
    @Scheduled(fixedRate = HEALTH_CHECK_INTERVAL)
    public void checkAllInstances() {
        try {
            // 1. 检查所有实例的健康状态
            checkIndividualInstances();

            // 2. 检查服务集群健康状态
            //checkServiceClusterHealth();
        } catch (Exception e) {
            log.error("健康检查任务执行失败", e);
        }
    }

    /**
     * 检查单个实例的健康状态
     */
    private void checkIndividualInstances() {
        instanceManager.getAllInstances().values().stream().filter(t -> StrUtil.isNotBlank(t.getHealthCheckUrl())).forEach(instance -> {
            // 处理已标记为不健康且超过延迟时间的实例
            if (instance.getHealthStatus() == InstanceInfo.HealthStatus.UNHEALTHY) {
                handleDelayedInstanceDeregister(instance);
                return;
            }

            // 执行健康检查
            try {
                ResponseEntity<String> response = restTemplate.getForEntity(
                        instance.getHttpHealthCheckUrl(), String.class);

                if (response.getStatusCode().is2xxSuccessful()) {
                    handleHealthyInstance(instance);
                } else {
                    handleUnhealthyInstance(instance);
                }
            } catch (Exception e) {
                log.warn("健康检查失败: {} | 错误: {}", instance.getHealthCheckUrl(), e.getMessage());
                handleUnhealthyInstance(instance);
            }
        });
    }

    /**
     * 处理健康的实例
     */
    private void handleHealthyInstance(InstanceInfo instance) {
        // 重置健康状态
        instance.markHealthy();
        log.info("实例恢复健康: {}@{}:{}",
                instance.getServiceCode(), instance.getIp(), instance.getPort());
    }

    /**
     * 处理不健康的实例
     */
    private void handleUnhealthyInstance(InstanceInfo instance) {
        // 增加失败计数
        instance.incrementFailureCount();

        if (instance.getFailureCount() >= MAX_FAILURE_COUNT) {
            // 达到阈值：标记为不健康
            instance.markUnhealthy();

            log.error("实例连续失败{}次，标记为不健康: {}@{}:{}",
                    MAX_FAILURE_COUNT,
                    instance.getServiceCode(),
                    instance.getIp(),
                    instance.getPort());
            // 上报到 Nacos
//            reportInstanceUnhealthy(
//                    serverAdd, // 需从 instance 获取 Nacos 地址
//                    instance.getNamespace(),
//                    instance.getServiceCode(),
//                    instance.getIp(),
//                    instance.getPort()
//            );
            // 检查服务集群状态
//            checkServiceClusterStatus(instance);
        } else {
            // 未达阈值：记录警告
            log.warn("实例检查失败: {}/{} | {}@{}:{}",
                    instance.getFailureCount(),
                    MAX_FAILURE_COUNT,
                    instance.getServiceCode(),
                    instance.getIp(),
                    instance.getPort());
        }
    }

    /**
     * 处理延迟注销的实例
     */
    private void handleDelayedInstanceDeregister(InstanceInfo instance) {
        long unhealthyDuration = System.currentTimeMillis() - instance.getUnhealthyTime();
        deregisterInstance(instance);
//        if (unhealthyDuration > INSTANCE_DEREGISTER_DELAY) {
//            deregisterInstance(instance);
//        } else {
//            log.info("实例等待注销: {}@{}:{} | 已等待: {}/{}秒",
//                    instance.getServiceCode(), instance.getIp(), instance.getPort(),
//                    unhealthyDuration / 1000, INSTANCE_DEREGISTER_DELAY / 1000);
//        }
    }

    /**
     * 注销单个实例
     */
    private void deregisterInstance(InstanceInfo instance) {
        try {
            log.warn("注销实例: {}@{}:{}",
                    instance.getServiceCode(), instance.getIp(), instance.getPort());

            // 从Nacos注销
            NamingService namingService = namingServiceManager.getNamingService(instance.getNamespace());
            namingService.deregisterInstance(
                    instance.getServiceCode(),
                    instance.getIp(),
                    instance.getPort()
            );
            MicroNacos microNacos = microNacosService.getByNamespaceCode(instance.getNamespace(), instance.getServiceCode());
            microNacos.setActiveStatus(ActiveStatusEnum.OFF.getCode());
            microNacosService.edit(microNacos);

            // 从管理器中移除
            instanceManager.removeInstance(
                    instance.getNamespace(),
                    instance.getServiceCode(),
                    instance.getIp(),
                    instance.getPort()
            );
        } catch (Exception e) {
            log.error("注销实例失败: {}", instance, e);
        }
    }

    /**
     * 检查服务集群状态
     */
    private void checkServiceClusterStatus(InstanceInfo instance) {
        String namespace = instance.getNamespace();
        String serviceCode = instance.getServiceCode();
        String serviceKey = namespace + "@" + serviceCode;

        // 获取服务的所有实例
        List<InstanceInfo> instances = instanceManager.getServiceInstances(namespace, serviceCode);

        // 统计健康实例数量
        long healthyCount = instances.stream()
                .filter(inst -> inst.getHealthStatus() == InstanceInfo.HealthStatus.HEALTHY)
                .count();

        // 如果没有健康实例，标记服务待注销
        if (healthyCount == 0) {
            servicesToDeregister.put(serviceKey, System.currentTimeMillis());
            log.error("服务所有实例不可用，标记待注销: {}", serviceKey);
        }
    }

    /**
     * 检查服务集群健康状态
     */
    private void checkServiceClusterHealth() {
        servicesToDeregister.entrySet().removeIf(entry -> {
            String serviceKey = entry.getKey();
            long markTime = entry.getValue();

            // 拆分命名空间和服务名
            String[] parts = serviceKey.split("@", 2);
            String namespace = parts[0];
            String serviceCode = parts[1];

            // 获取服务的所有实例
            List<InstanceInfo> instances = instanceManager.getServiceInstances(namespace, serviceCode);

            // 统计健康实例数量
            long healthyCount = instances.stream()
                    .filter(inst -> inst.getHealthStatus() == InstanceInfo.HealthStatus.HEALTHY)
                    .count();

            // 如果有健康实例，移出注销列表
            if (healthyCount > 0) {
                log.info("服务恢复健康实例: {} | 健康实例数: {}", serviceKey, healthyCount);
                return true; // 移出注销列表
            }

            // 检查是否达到服务注销延迟时间
            long unhealthyDuration = System.currentTimeMillis() - markTime;
            if (unhealthyDuration > SERVICE_DEREGISTER_DELAY) {
                deregisterService(namespace, serviceCode);
                return true; // 移出注销列表
            }

            // 记录等待状态
            log.info("服务等待注销: {} | 已等待: {}/{}秒",
                    serviceKey,
                    unhealthyDuration / 1000,
                    SERVICE_DEREGISTER_DELAY / 1000);
            return false; // 保留在注销列表
        });
    }

    /**
     * 注销整个服务
     */
    private void deregisterService(String namespace, String serviceCode) {
        String serviceKey = namespace + "@" + serviceCode;

        try {
            log.warn("开始注销服务: {}", serviceKey);
            NamingService namingService = namingServiceManager.getNamingService(namespace);

            // 获取所有实例
            List<InstanceInfo> instances = instanceManager.getServiceInstances(namespace, serviceCode);

            // 注销每个实例
            for (InstanceInfo instance : instances) {
                try {
                    log.info("注销服务实例: {}@{}:{}",
                            instance.getServiceCode(), instance.getIp(), instance.getPort());

                    namingService.deregisterInstance(
                            instance.getServiceCode(),
                            instance.getIp(),
                            instance.getPort()
                    );
                } catch (NacosException e) {
                    log.error("注销实例失败: {}", instance, e);
                }

                // 从管理器中移除
                instanceManager.removeInstance(
                        instance.getNamespace(),
                        instance.getServiceCode(),
                        instance.getIp(),
                        instance.getPort()
                );
            }

            log.warn("服务注销完成: {}", serviceKey);
        } catch (NacosException e) {
            log.error("获取NamingService失败: {} | {}", namespace, e.getMessage());
        }
    }

    public void reportInstanceUnhealthy(String nacosServer, String namespaceId, String serviceName, String ip, int port) {

        String url = nacosServer + "/nacos/v2/ns/health/instance"
                + "?namespaceId=" + namespaceId
                + "&serviceName=" + serviceName
                + "&ip=" + ip
                + "&port=" + port
                + "&healthy=false";

        try {
//            HttpHeaders headers = new HttpHeaders();
//            headers.set("username", username);
//            headers.set("password",password);
//            HttpEntity<?> entity = new HttpEntity<>(headers);
//            restTemplate.exchange(url, HttpMethod.PUT, entity, String.class);
//            restTemplate.put(url, null);
//            log.info("已上报实例不健康: {}@{}:{}", serviceName, ip, port);

             webClient.put()
                    .uri(uriBuilder -> {
                        uriBuilder.path("/nacos/v2/ns/health/instance");
                        uriBuilder.queryParam("namespaceId", namespaceId);
                        uriBuilder.queryParam("serviceName", serviceName);
                        uriBuilder.queryParam("ip", ip);
                        uriBuilder.queryParam("port", port);
                        uriBuilder.queryParam("healthy", "false");
                        return uriBuilder.build();
                    })
                     .header("username",username)
                     .header("password",password)
                    .retrieve()
                    .bodyToMono(Map.class)
                    .block(Duration.ofSeconds(5));
            log.info("已上报实例不健康: {}@{}:{}", serviceName, ip, port);
        } catch (Exception e) {
            log.error("上报实例不健康失败: {}@{}:{}，原因: {}", serviceName, ip, port, e.getMessage());
        }
    }
}